//**********************************************************************************************************
//
//! \file DrvTIMER.c
//! \brief This file provides all the TIMER firmware functions.
//! \version V1.06
//! \date 14. March 2011
//! \author NuMicro MCU Software Team
//! \copy
//!
//! Copyright (c)  2011, Nuvoton Technology Corp.
//! All rights reserved.
//
//**********************************************************************************************************

//**********************************************************************************************************
//
//!  Includes of local headers	
//							                                                   
//**********************************************************************************************************
#include "DrvTimer.h"
#include "DrvSys.h"
#include "assert.h"

//**********************************************************************************************************
//
//!  Macro, type and constant definitions 
// 								                                                   
//**********************************************************************************************************
#define TIMER_EVENT_COUNT   1
#define TIMER_OFFSET        0x20

//**********************************************************************************************************
//
//!  Global file scope (static) variables 
//  								                                                   
//**********************************************************************************************************
static TIMER_EVENT_T tTime0Event[TIMER_EVENT_COUNT],
                     tTime1Event[TIMER_EVENT_COUNT],
                     tTime2Event[TIMER_EVENT_COUNT],
                     tTime3Event[TIMER_EVENT_COUNT];

int32_t volatile bIsTimer0Used        = FALSE,
                 bIsTimer1Used        = FALSE,
                 bIsTimer2Used        = FALSE,
                 bIsTimer3Used        = FALSE,
                 bIsSetTime0Event     = FALSE,
                 bIsSetTime1Event     = FALSE,
                 bIsSetTime2Event     = FALSE,
                 bIsSetTime3Event     = FALSE;

static uint32_t volatile uTimer0Tick = 0,
                         uTimer1Tick = 0,
                         uTimer2Tick = 0,
                         uTimer3Tick = 0,
                         uTime0EventCount = 0,
                         uTime1EventCount = 0,
                         uTime2EventCount = 0,
                         uTime3EventCount = 0;

static uint32_t volatile _sys_uTimer0TickPerSecond,
                         _sys_uTimer1TickPerSecond,
                         _sys_uTimer2TickPerSecond, 
                         _sys_uTimer3TickPerSecond;

uint32_t volatile u32EXTClockFreq = 12000000;
static uint32_t wdt_param;
static WDT_CALLBACK fnCallBack_WDT;


#ifdef __ICCARM__
static inline TIMER_TypeDef * _getBase(E_TIMER_CHANNEL ch)
#else
static __inline TIMER_TypeDef * _getBase(E_TIMER_CHANNEL ch)
#endif
{
    switch(ch) {
        case E_TMR0:
            return((TIMER_TypeDef *)TIMER0_BASE);
        case E_TMR1:
            return((TIMER_TypeDef *)TIMER1_BASE);
        case E_TMR2:
            return((TIMER_TypeDef *)TIMER2_BASE);
        case E_TMR3:
            return((TIMER_TypeDef *)TIMER3_BASE);               
    }
    return(NULL);
}

//**********************************************************************************************************
//
//! \brief  Get the timer clock from the specified timer channel. 
//!
//! \param  ch   E_TIMER_CHANNEL, it could be E_TMR0/E_TMR1/E_TMR2/E_TMR3 
//!
//! \retval u32clk                                  The timer clock (Hz)                           
//!         E_DRVTIMER_CHANNEL                      Invalid Timer channel 
//
//**********************************************************************************************************
static uint32_t GetTimerClock(E_TIMER_CHANNEL ch)
{
    volatile uint8_t u8ClockcSrc = 0;
    volatile uint32_t u32clk = 0;

	assert_param(ch <= E_TMR3);
    
    if(ch == E_TMR0)
        u8ClockcSrc = CLK->CLKSEL1_BITS.TMR0_S;
    else if(ch == E_TMR1)
        u8ClockcSrc = CLK->CLKSEL1_BITS.TMR1_S;        
    else if(ch == E_TMR2)
        u8ClockcSrc = CLK->CLKSEL2_BITS.TMR2_S; 
    else if(ch == E_TMR3)
        u8ClockcSrc = CLK->CLKSEL2_BITS.TMR3_S;      

        
    switch(u8ClockcSrc) {
        case 0:
            u32clk = DrvSYS_GetExtClockFreq() ;
            break;
        case 1:
            u32clk = __RTC_XTAL;
            break;
        case 2:
            u32clk = __IRC10K  ;
            break;
        case 3:
            u32clk = u32EXTClockFreq;
            break;
        default:
            u32clk = __IRC12M;    
    }                
                
    return u32clk;
}

//**********************************************************************************************************
//
//! \brief  Calculate the Timer Compare Value and pre-scale value for Timer. 
//!
//! \param  u32ClockValue    Input the clock value of Timer 
//! \param  u32TicksPerSec   Specify the ticks per second of Timer 
//!
//! \retval Return 32 bits unsigned integer where 
//!                  bits [ 0:23] - The Timer Compare Value(TCMPR) for Timer
//!                  bits [24:31] - The pre-scale value for Timer                          
//!        (uint32_t)-1                            Out of range  
//
//**********************************************************************************************************
static uint32_t CalTimerInitValue(uint32_t u32ClockValue, uint32_t u32TicksPerSec)
{
    uint32_t u32PreScale;
    uint32_t u32TCMPRValue;

    if ((u32ClockValue < 2) || (u32TicksPerSec == 0))
        return (uint32_t)-1;

    for (u32PreScale=1; u32PreScale<256; u32PreScale++)
    {
        u32TCMPRValue = u32ClockValue / (u32TicksPerSec * u32PreScale);

        /* The TCMPR value must > 1 */
        if ((u32TCMPRValue > 1) && (u32TCMPRValue < 0x1000000))
            return (((u32PreScale-1) << 24) | u32TCMPRValue);
    }
    
    return (uint32_t)-1;
}

//**********************************************************************************************************
//
//! \brief  The TIMER0 default IRQ, declared in startup_NUC1xx.s . 
//!
//! \param  None
//!
//! \retval None 
//
//**********************************************************************************************************
void TMR0_IRQHandler(void)
{
    int32_t i;
   
    TIMER0->ISR = 3;

    uTimer0Tick++;
    if (bIsSetTime0Event)                       /* Timer Event Handle */
    {
        for (i=0; i<TIMER_EVENT_COUNT; i++)
        {
            if (tTime0Event[i].active)
            {
                tTime0Event[i].curTick--;
                if (tTime0Event[i].curTick == 0)
                {
                    (*tTime0Event[i].funPtr)(tTime0Event[i].transParam);
                    tTime0Event[i].curTick = tTime0Event[i].initTick;
                }
            }
        }
    }
}

//**********************************************************************************************************
//
//! \brief  The TIMER1 default IRQ, declared in startup_NUC1xx.s . 
//!
//! \param  None
//!
//! \retval None 
//
//**********************************************************************************************************
void TMR1_IRQHandler(void)
{
    int32_t i;
    
    TIMER1->ISR = 3;

    uTimer1Tick++;
    if (bIsSetTime1Event)                       /* Timer Event Handle */
    {
        for (i=0; i<TIMER_EVENT_COUNT; i++)
        {
            if (tTime1Event[i].active)
            {
                tTime1Event[i].curTick--;
                if (tTime1Event[i].curTick == 0)
                {
                    (*tTime1Event[i].funPtr)(tTime1Event[i].transParam);
                    tTime1Event[i].curTick = tTime1Event[i].initTick;
                }
            }
        }
    }
}

//**********************************************************************************************************
//
//! \brief  The TIMER2 default IRQ, declared in startup_NUC1xx.s . 
//!
//! \param  None
//!
//! \retval None 
//
//**********************************************************************************************************
void TMR2_IRQHandler(void)
{
    int32_t i;
    
    TIMER2->ISR = 3;

    uTimer2Tick++;
    if (bIsSetTime2Event)                       /* Timer Event Handle */
    {
        for (i=0; i<TIMER_EVENT_COUNT; i++)
        {
            if (tTime2Event[i].active)
            {
                tTime2Event[i].curTick--;
                if (tTime2Event[i].curTick == 0)
                {
                    (*tTime2Event[i].funPtr)(tTime2Event[i].transParam);
                    tTime2Event[i].curTick = tTime2Event[i].initTick;
                }
            }
        }
    }
}

//**********************************************************************************************************
//
//! \brief  The TIMER3 default IRQ, declared in startup_NUC1xx.s . 
//!
//! \param  None
//!
//! \retval None                          
//
//**********************************************************************************************************
void TMR3_IRQHandler(void)
{
    int32_t i;
    
    TIMER3->ISR = 3;

    uTimer3Tick++;
    if (bIsSetTime3Event)                       /* Timer Event Handle */
    {
        for (i=0; i<TIMER_EVENT_COUNT; i++)
        {
            if (tTime3Event[i].active)
            {
                tTime3Event[i].curTick--;
                if (tTime3Event[i].curTick == 0)
                {
                    (*tTime3Event[i].funPtr)(tTime3Event[i].transParam);
                    tTime3Event[i].curTick = tTime3Event[i].initTick;
                }
            }
        }
    }
}

//**********************************************************************************************************
//
//! \brief  This API is here for compatibility reason. 
//!
//! \param  None  
//!
//! \retval None                       
//
//**********************************************************************************************************
void DrvTIMER_Init(void)
{
    return;
}

//**********************************************************************************************************
//
//! \brief  Open the specified timer channel with specified operation mode. 
//!
//! \param  ch                E_TIMER_CHANNEL, it could be E_TMR0/E_TMR1/E_TMR2/E_TMR3
//! \param  uTicksPerSecond   This value means how many timer interrupt ticks in one second 
//! \param  op_mode           E_TIMER_OPMODE, E_ONESHOT_MODE/E_PERIODIC_MODE/E_TOGGLE_MODE/E_CONTINUOUS_MODE
//!
//! \retval E_SUCCESS                               Operation successful                           
//!         E_DRVTIMER_CHANNEL                      Invalid Timer channel 
//!         E_DRVTIMER_CLOCK_RATE                   Calculate initial value fail  
//
//**********************************************************************************************************
int32_t DrvTIMER_Open(E_TIMER_CHANNEL ch, uint32_t uTicksPerSecond, E_TIMER_OPMODE op_mode)
{
    uint32_t i;
    uint32_t uRegTcmpr;

	assert_param(ch <= E_TMR3);

    switch (ch)
    {
        case E_TMR0:
        {
            if (bIsTimer0Used != FALSE)
                return E_DRVTIMER_EIO;
            
            bIsTimer0Used = TRUE;
           
            CLK->APBCLK_BITS.TMR0_EN = 1;
                                                              
            TIMER0->CTL = 0;                  /* disable timer */
            TIMER0->ISR = 0x13;                             /* write 1 to clear for safty */
            uTime0EventCount = 0;                               
            for (i=0; i<TIMER_EVENT_COUNT; i++)
            {
                tTime0Event[i].active = FALSE;
            }

            uTimer0Tick = 0;            
            _sys_uTimer0TickPerSecond = uTicksPerSecond;
                    
            uRegTcmpr = CalTimerInitValue(GetTimerClock(E_TMR0), uTicksPerSecond); 
            if (uRegTcmpr > 0xFFFFFF)       
            {
                return E_DRVTIMER_CLOCK_RATE;           
            }

            TIMER0->CMPR = (uRegTcmpr & 0xFFFFFF);  
            TIMER0->CTL_BITS.MODE_SEL = op_mode;        
            break;
        }

        case E_TMR1:
        {
            if (bIsTimer1Used != FALSE)
                return E_DRVTIMER_EIO;

            bIsTimer1Used = TRUE;

            CLK->APBCLK_BITS.TMR1_EN = 1;
            
            TIMER1->CTL = 0;                  /* disable timer */
            TIMER1->ISR = 0x13;                             /* write 1 to clear for safty */
            uTime1EventCount = 0;
            for (i=0; i<TIMER_EVENT_COUNT; i++)
            {
                tTime1Event[i].active = FALSE;
            }

            uTimer1Tick = 0;
            _sys_uTimer1TickPerSecond = uTicksPerSecond;

            uRegTcmpr = CalTimerInitValue(GetTimerClock(E_TMR1), uTicksPerSecond); 
            if(uRegTcmpr > 0xFFFFFF)        
            {
                return E_DRVTIMER_CLOCK_RATE;           
            }

            TIMER1->CMPR = (uRegTcmpr & 0xFFFFFF);  
            TIMER1->CTL_BITS.MODE_SEL = op_mode; 
            break;
        }

        case E_TMR2:
        {
            if (bIsTimer2Used != FALSE)
                return E_DRVTIMER_EIO;    

            bIsTimer2Used = TRUE;

            CLK->APBCLK_BITS.TMR2_EN =1;
            
            TIMER2->CTL = 0;                  /* disable timer */
            TIMER2->ISR = 0x13;                             /* write 1 to clear for safty */
            uTime2EventCount = 0;
            for (i=0; i<TIMER_EVENT_COUNT; i++)
            {
                tTime2Event[i].active = FALSE;
            }

            uTimer2Tick = 0;
            _sys_uTimer2TickPerSecond = uTicksPerSecond;

            uRegTcmpr = CalTimerInitValue(GetTimerClock(E_TMR2), uTicksPerSecond); 
            if(uRegTcmpr > 0xFFFFFF)        
            {
                return E_DRVTIMER_CLOCK_RATE;           
            }

            TIMER2->CMPR = (uRegTcmpr & 0xFFFFFF);  
            TIMER2->CTL_BITS.MODE_SEL = op_mode; 
            break;
        }

        case E_TMR3:
        {
            if (bIsTimer3Used != FALSE)
                return E_DRVTIMER_EIO;

            bIsTimer3Used = TRUE;

            CLK->APBCLK_BITS.TMR3_EN = 1;

            TIMER3->CTL = 0;                  /* disable timer */
            TIMER3->ISR = 0x13;                             /* write 1 to clear for safty */
            uTime3EventCount = 0;
            for (i=0; i<TIMER_EVENT_COUNT; i++)
            {
                tTime3Event[i].active = FALSE;
            }

            uTimer3Tick = 0;
            _sys_uTimer3TickPerSecond = uTicksPerSecond;
            
            uRegTcmpr = CalTimerInitValue(GetTimerClock(E_TMR3), uTicksPerSecond); 
            if(uRegTcmpr > 0xFFFFFF)        
            {
                return E_DRVTIMER_CLOCK_RATE;           
            }

            TIMER3->CMPR = (uRegTcmpr & 0xFFFFFF);  
            TIMER3->CTL_BITS.MODE_SEL = op_mode;    
            break;
        }

    }

    return E_SUCCESS;
}

//**********************************************************************************************************
//
//! \brief  This function is used to open the timer channel with the specified operation mode.  
//!         This API exist for compatibility reason. 
//!
//! \param  ch                  E_TIMER_CHANNEL, it could be E_TMR0/E_TMR1/E_TMR2/E_TMR3 
//! \param  uCounterBoundary    The parameter is used to determine how many counts occurred will 
//!                             toggle once timer interrupt.
//! \param  op_mode             E_TIMER_OPMODE, E_ONESHOT_MODE/E_PERIODIC_MODE/E_CONTINUOUS_MODE
//!
//! \retval E_SUCCESS                               Operation successful                           
//!         E_DRVTIMER_CHANNEL                      Invalid Timer channel 
//!         E_DRVTIMER_EIO                          Timer has not been initialized 
//
//**********************************************************************************************************
int32_t DrvTIMER_OpenCounter(E_TIMER_CHANNEL ch, uint32_t uCounterBoundary, E_TIMER_OPMODE op_mode)
{
    uint32_t i;


    assert_param(op_mode != E_TOGGLE_MODE);
    assert_param(ch <= E_TMR3);
                        
    switch (ch)
    {
        case E_TMR0:
        {
            if (bIsTimer0Used != FALSE)
                return E_DRVTIMER_EIO;
                                   
            bIsTimer0Used = TRUE;
           
            CLK->APBCLK_BITS.TMR0_EN = 1;

            TIMER0->ISR_BITS.TMR_IS = 1;                /* Write 1 to clear for safty */
            
            TIMER0->CTL_BITS.MODE_SEL       = op_mode;  /* Set operation mode */
            TIMER0->CMPR                = uCounterBoundary;
            TIMER0->PRECNT              = 0;
            TIMER0->CTL_BITS.TMR_EN     = 1;             
            uTime0EventCount = 0;
            for (i=0; i<TIMER_EVENT_COUNT; i++)
            {
                tTime0Event[i].active = FALSE;
            }
            uTimer0Tick = 0;
            break;
        }

        case E_TMR1:
        {
            if (bIsTimer1Used != FALSE)
                return E_DRVTIMER_EIO;

                                   
            bIsTimer1Used = TRUE;
           
            CLK->APBCLK_BITS.TMR1_EN = 1;


            TIMER1->ISR_BITS.TMR_IS         = 1;        /* Write 1 to clear for safty */            
            TIMER1->CTL_BITS.MODE_SEL       = op_mode;  /* Set operation mode */
            TIMER1->CMPR            = uCounterBoundary;
            TIMER1->PRECNT  = 0;
            TIMER1->CTL_BITS.TMR_EN     = 1;                 
            uTime1EventCount = 0;
            for (i=0; i<TIMER_EVENT_COUNT; i++)
            {
                tTime1Event[i].active = FALSE;
            }
            uTimer1Tick = 0;
            break;
        }

        case E_TMR2:
        {
            if (bIsTimer2Used != FALSE)
                return E_DRVTIMER_EIO;

                                   
            bIsTimer2Used = TRUE;
           
            CLK->APBCLK_BITS.TMR2_EN = 1;

            TIMER2->ISR_BITS.TMR_IS         = 1;        /* Write 1 to clear for safty */            
            TIMER2->CTL_BITS.MODE_SEL       = op_mode;  /* Set operation mode */
            TIMER2->CMPR            = uCounterBoundary;
            TIMER2->PRECNT  = 0;
            TIMER2->CTL_BITS.TMR_EN     = 1;                 
            uTime2EventCount = 0;
            for (i=0; i<TIMER_EVENT_COUNT; i++)
            {
                tTime2Event[i].active = FALSE;
            }
            uTimer2Tick = 0;
            break;
        }

        case E_TMR3:
        {
            if (bIsTimer3Used != FALSE)
                return E_DRVTIMER_EIO;

                                   
            bIsTimer3Used = TRUE;
           
            CLK->APBCLK_BITS.TMR3_EN = 1;

            TIMER3->ISR_BITS.TMR_IS         = 1;        /* Write 1 to clear for safty */            
            TIMER3->CTL_BITS.MODE_SEL       = op_mode;  /* Set operation mode */
            TIMER3->CMPR            = uCounterBoundary;
            TIMER3->PRECNT  = 0;
            TIMER3->CTL_BITS.TMR_EN     = 1;                 
            uTime3EventCount = 0;
            for (i=0; i<TIMER_EVENT_COUNT; i++)
            {
                tTime3Event[i].active = FALSE;
            }
            uTimer3Tick = 0;
            break;
        }

    }
    DrvTIMER_EnableExternalEventMode(ch, E_TIMER_EVENT_EDGE_RISING);

    return E_SUCCESS;
}

//**********************************************************************************************************
//
//! \brief  The function is used to close the timer channel. 
//!
//! \param  ch    E_TIMER_CHANNEL, it could be E_TMR0/E_TMR1/E_TMR2/E_TMR3 
//!
//! \retval E_SUCCESS                               Operation successful                           
//!         E_DRVTIMER_CHANNEL                      Invalid Timer channel 
//
//**********************************************************************************************************
int32_t DrvTIMER_Close(E_TIMER_CHANNEL ch)
{
    assert_param(ch <= E_TMR3);
    switch (ch)
    {
        case E_TMR0:
        {
            bIsTimer0Used       = FALSE;
            bIsSetTime0Event    = FALSE;
            break;
        }

        case E_TMR1:
        {
            bIsTimer1Used       = FALSE;
            bIsSetTime1Event    = FALSE;
            break;
        }

        case E_TMR2:
        {
            bIsTimer2Used       = FALSE;
            bIsSetTime2Event    = FALSE;
            break;
        }

        case E_TMR3:
        {
            bIsTimer3Used       = FALSE;
            bIsSetTime3Event    = FALSE;
            break;
        }

    }
    
    DrvTIMER_DisableInt(ch, E_TIMER_BOTH_INT);
    DrvTIMER_Reset(ch);
    return E_SUCCESS;
}

//**********************************************************************************************************
//
//! \brief   Install the interrupt callback function of the specified timer channel.                
//!          And trigger timer callback functuion when interrupt occur specified times.
//!
//! \param  ch                E_TIMER_CHANNEL, it could be E_TMR0/E_TMR1/E_TMR2/E_TMR3 
//! \param  uInterruptTicks   Number of timer interrupt occurred
//! \param  pTimerCallback    The function pointer of the interrupt callback function 
//! \param  parameter         A parameter of the callback function 
//!
//! \retval uTimerEventNo                           The timer event number                            
//!         E_DRVTIMER_EVENT_FULL                   The timer event is full 
//
//**********************************************************************************************************
int32_t DrvTIMER_SetTimerEvent(E_TIMER_CHANNEL ch, uint32_t uInterruptTicks, TIMER_CALLBACK pTimerCallback, uint32_t parameter)
{
    volatile int32_t i;
    int32_t uTimerEventNo = 0;

    switch (ch)
    {
        case E_TMR0:
        {
            if (uTime0EventCount >= TIMER_EVENT_COUNT)
                return E_DRVTIMER_EVENT_FULL;

            bIsSetTime0Event = TRUE;
            uTime0EventCount++;
            for (i=0; i<TIMER_EVENT_COUNT; i++)
            {
                if (tTime0Event[i].active  == FALSE)
                {
                    tTime0Event[i].active       = TRUE;
                    tTime0Event[i].initTick     = uInterruptTicks;
                    tTime0Event[i].curTick      = uInterruptTicks;
                    tTime0Event[i].funPtr       = (TIMER_CALLBACK)pTimerCallback;
                    tTime0Event[i].transParam   = parameter;
                    uTimerEventNo = i;
                    break;
                }
            }
            break;
        }

        case E_TMR1:
        {
            if (uTime1EventCount >= TIMER_EVENT_COUNT)
                return E_DRVTIMER_EVENT_FULL;

            bIsSetTime1Event = TRUE;
            uTime1EventCount++;
            for (i=0; i<TIMER_EVENT_COUNT; i++)
            {
                if (tTime1Event[i].active   == FALSE)
                {
                    tTime1Event[i].active       = TRUE;
                    tTime1Event[i].initTick     = uInterruptTicks;
                    tTime1Event[i].curTick      = uInterruptTicks;
                    tTime1Event[i].funPtr       = (TIMER_CALLBACK)pTimerCallback;
                    tTime1Event[i].transParam   = parameter;
                    uTimerEventNo = i;
                    break;
                }
            }
            break;
        }

        case E_TMR2:
        {
            if (uTime2EventCount >= TIMER_EVENT_COUNT)
                return E_DRVTIMER_EVENT_FULL;

            bIsSetTime2Event = TRUE;
            uTime2EventCount++;
            for (i=0; i<TIMER_EVENT_COUNT; i++)
            {
                if (tTime2Event[i].active   == FALSE)
                {
                    tTime2Event[i].active       = TRUE;
                    tTime2Event[i].initTick     = uInterruptTicks;
                    tTime2Event[i].curTick      = uInterruptTicks;
                    tTime2Event[i].funPtr       = (TIMER_CALLBACK)pTimerCallback;
                    tTime2Event[i].transParam   = parameter;
                    uTimerEventNo = i;
                    break;
                }
            }
            break;
        }

        case E_TMR3:
        {
            if (uTime3EventCount >= TIMER_EVENT_COUNT)
                return E_DRVTIMER_EVENT_FULL;

            bIsSetTime3Event = TRUE;
            uTime3EventCount++;
            for (i=0; i<TIMER_EVENT_COUNT; i++)
            {
                if (tTime3Event[i].active   == FALSE)
                {
                    tTime3Event[i].active       = TRUE;
                    tTime3Event[i].initTick     = uInterruptTicks;
                    tTime3Event[i].curTick      = uInterruptTicks;
                    tTime3Event[i].funPtr       = (TIMER_CALLBACK)pTimerCallback;
                    tTime3Event[i].transParam   = parameter;
                    uTimerEventNo = i;
                    break;
                }
            }
            break;
        }

        default:
        {
            break;
        }
    }

    return uTimerEventNo;
}

//**********************************************************************************************************
//
//! \brief  Clear the timer event of the specified timer channel. 
//!
//! \param  ch             E_TIMER_CHANNEL, it could be E_TMR0/E_TMR1/E_TMR2/E_TMR3 
//! \param  uTimerEventNo  The timer event number 
//!
//! \retval None                           
//
//**********************************************************************************************************
void DrvTIMER_ClearTimerEvent(E_TIMER_CHANNEL ch, uint32_t uTimerEventNo)
{
    switch (ch)
    {
        case E_TMR0:
        {
            tTime0Event[uTimerEventNo].active = FALSE;
            uTime0EventCount--;
            if (uTime0EventCount == 0)
            {
                bIsSetTime0Event = FALSE;
            }
            break;
        }

        case E_TMR1:
        {
            tTime1Event[uTimerEventNo].active = FALSE;
            uTime1EventCount--;
            if (uTime1EventCount == 0)
            {
                bIsSetTime1Event = FALSE;
            }
            break;
        }

        case E_TMR2:
        {
            tTime2Event[uTimerEventNo].active = FALSE;
            uTime2EventCount--;
            if (uTime2EventCount == 0)
            {
                bIsSetTime2Event = FALSE;
            }
            break;
        }

        case E_TMR3:
        {
            tTime3Event[uTimerEventNo].active = FALSE;
            uTime3EventCount--;
            if (uTime3EventCount == 0)
            {
                bIsSetTime3Event = FALSE;
            }
            break;
        }

        default:
        {
            break;
        }
    }
}

//**********************************************************************************************************
//
//! \brief  This function is used to enable the specified timer interrupt. 
//!
//! \param  ch    E_TIMER_CHANNEL, it could be E_TMR0/E_TMR1/E_TMR2/E_TMR3 
//!
//! \retval E_SUCCESS                               Operation successful                           
//!         E_DRVTIMER_CHANNEL                      Invalid Timer channel
//
//**********************************************************************************************************
int32_t DrvTIMER_EnableInt(E_TIMER_CHANNEL ch, E_TIMER_INT eInterrupt)
{
    TIMER_TypeDef *base = _getBase(ch);

    base->ISR = eInterrupt;  // clear pending interrupt
    base->IER |= eInterrupt;

    switch(ch)
    {
        case E_TMR0:
            NVIC_EnableIRQ(TMR0_IRQn);
            break;  
        case E_TMR1:
            NVIC_EnableIRQ(TMR1_IRQn);
            break;  
        case E_TMR2:
            NVIC_EnableIRQ(TMR2_IRQn);
            break;  
        case E_TMR3:
            NVIC_EnableIRQ(TMR3_IRQn);
            break;              
    
    }

    return(0);
}

//**********************************************************************************************************
//
//! \brief  This function is used to disable the specified timer interrupt. 
//!
//! \param  ch    E_TIMER_CHANNEL, it could be E_TMR0/E_TMR1/E_TMR2/E_TMR3 
//!
//! \retval E_SUCCESS                               Operation successful                           
//!         E_DRVTIMER_CHANNEL                      Invalid Timer channel
//
//**********************************************************************************************************
int32_t DrvTIMER_DisableInt(E_TIMER_CHANNEL ch, E_TIMER_INT eInterrupt)
{
    TIMER_TypeDef *base = _getBase(ch); 
        
    base->IER &= ~eInterrupt;

    if(base->IER == 0) {
    
        switch(ch)
        {
            case E_TMR0:
                NVIC_DisableIRQ(TMR0_IRQn);
                break;  
            case E_TMR1:
                NVIC_DisableIRQ(TMR1_IRQn);
                break;  
            case E_TMR2:
                NVIC_DisableIRQ(TMR2_IRQn);
                break;  
            case E_TMR3:
                NVIC_DisableIRQ(TMR3_IRQn);
                break;              
        }           
    }   
    return(0);
}

//**********************************************************************************************************
//
//! \brief  Get the interrupt flag status from the specified timer channel. 
//!
//! \param  ch   E_TIMER_CHANNEL, it could be E_TMR0/E_TMR1/E_TMR2/E_TMR3 
//!
//! \retval iIntStatus                              0:No interrupt / 1:Interrupt occurred                            
//!         E_DRVTIMER_CHANNEL                      Invalid Timer channel   
//
//**********************************************************************************************************
int32_t DrvTIMER_GetIntFlag(E_TIMER_CHANNEL ch)
{
    int32_t iIntStatus = 0;

    assert_param(ch <= E_TMR3);

    if (ch == E_TMR0 )
        iIntStatus =  TIMER0->ISR;
    else if(ch == E_TMR1 )
        iIntStatus =  TIMER1->ISR;
    else if(ch == E_TMR2 )
        iIntStatus =  TIMER2->ISR;
    else if(ch == E_TMR3 )
        iIntStatus =  TIMER3->ISR;


    return iIntStatus;
}

//**********************************************************************************************************
//
//! \brief  Clear the interrupt flag of the specified timer channel. 
//!
//! \param  ch    E_TIMER_CHANNEL, it could be E_TMR0/E_TMR1/E_TMR2/E_TMR3 
//!
//! \retval E_SUCCESS                               Operation successful                           
//!         E_DRVTIMER_CHANNEL                      Invalid Timer channel 
//
//**********************************************************************************************************
int32_t DrvTIMER_ClearIntFlag(E_TIMER_CHANNEL ch)
{
    assert_param(ch <= E_TMR3);    
	if (ch == E_TMR0 )
        TIMER0->ISR = 3;
    else if(ch == E_TMR1 )                                   
        TIMER1->ISR = 3;
    else if(ch == E_TMR2 )
        TIMER2->ISR = 3;
    else if(ch == E_TMR3 )
        TIMER3->ISR = 3;


    return E_SUCCESS;
}

//**********************************************************************************************************
//
//! \brief  Start to count the specified timer channel. 
//!
//! \param  ch    E_TIMER_CHANNEL, it could be E_TMR0/E_TMR1/E_TMR2/E_TMR3 
//!
//! \retval E_SUCCESS                               Operation successful                           
//!         E_DRVTIMER_CHANNEL                      Invalid Timer channel 
//
//**********************************************************************************************************
int32_t DrvTIMER_Start(E_TIMER_CHANNEL ch)
{
    TIMER_TypeDef *base = _getBase(ch);
    assert_param(base != NULL); 
    base->CTL_BITS.TMR_EN = 1;
    return(0);
}

//**********************************************************************************************************
//
//! \brief  This API can be replaced by calling DrvTIMER_Reset, then DrvTIMER_Start               
//!         Keep this for compatibility reason. 
//!
//! \param  ch   E_TIMER_CHANNEL, it could be E_TMR0/E_TMR1/E_TMR2/E_TMR3 
//!
//! \retval E_SUCCESS                               Operation successful                           
//!         E_DRVTIMER_CHANNEL                      Invalid Timer channel 
//
//**********************************************************************************************************
int32_t DrvTIMER_StartCounter(E_TIMER_CHANNEL ch)
{
    DrvTIMER_Reset(ch);

    return(DrvTIMER_Start(ch));
}

//**********************************************************************************************************
//
//! \brief          This function is used to get the number of interrupt occurred                         
//!                 after the timer interrupt function is enabled.                          .              
//!                 Thus DrvTIMER_EnableInt(ch) must been called in advance.
//!
//! \param  ch      E_TIMER_CHANNEL, it could be E_TMR0/E_TMR1/E_TMR2/E_TMR3 
//!
//! \retval         uTimerTick                              Return the interrupt ticks                            
//!                 E_DRVTIMER_CHANNEL                      Invalid Timer channel 
//
//**********************************************************************************************************
uint32_t DrvTIMER_GetIntTicks(E_TIMER_CHANNEL ch)
{
    assert_param(ch <= E_TMR3);
    switch (ch)
    {
        case E_TMR0:
        {
            return uTimer0Tick;  
        }

        case E_TMR1:
        {
            return uTimer1Tick;
        }   

        case E_TMR2:
        {
            return uTimer2Tick;
        }

        case E_TMR3:
        {
            return uTimer3Tick;
        }

    }
	return(0);
}

//**********************************************************************************************************
//
//! \brief  This function is used to clear interrupt ticks to 0. 
//!
//! \param  ch    E_TIMER_CHANNEL, it could be E_TMR0/E_TMR1/E_TMR2/E_TMR3 
//!
//! \retval E_SUCCESS                               Operation successful                           
//!         E_DRVTIMER_CHANNEL                      Invalid Timer channel  
//
//**********************************************************************************************************
int32_t DrvTIMER_ResetIntTicks(E_TIMER_CHANNEL ch)
{
    assert_param(ch <= E_TMR3);
    switch (ch)
    {
        case E_TMR0:
        {
            uTimer0Tick = 0;
            break;
        }

        case E_TMR1:
        {
            uTimer1Tick = 0;
            break;
        }

        case E_TMR2:
        {
            uTimer2Tick = 0;
            break;
        }

        case E_TMR3:
        {
            uTimer3Tick = 0;
            break;
        }
    }

    return E_SUCCESS;
}

//**********************************************************************************************************
//
//! \brief  Get the timer clock from the specified timer channel. 
//!
//! \param  ch - [in]   E_TIMER_CHANNEL, it could be E_TMR0/E_TMR1/E_TMR2/E_TMR3 
//!
//! \retval         u32clk                                  The timer clock (Hz)                           
//!                 E_DRVTIMER_CHANNEL                      Invalid Timer channel 
//
//**********************************************************************************************************
void DrvTIMER_Delay(E_TIMER_CHANNEL ch, uint32_t uIntTicks)
{
    uint32_t volatile btime;

    btime = DrvTIMER_GetIntTicks(ch);
    
    while (1)
    {
        if ((DrvTIMER_GetIntTicks(ch) - btime) > uIntTicks)
        {
            break;
        }
    }
}

//**********************************************************************************************************
//
//! \brief   Set the external clock frequence, it's used for timer clock source.                  
//!          User can change the timer clock source from the external clock source by calling       
//!          DrvSYS_SelectIPClockSource (...)     
//!
//! \param  u32ClockFreq    Set the clock frequence (Hz) for external clock source 
//!
//! \retval None                        
 
//
//**********************************************************************************************************
void DrvTIMER_SetEXTClockFreq(uint32_t u32ClockFreq)
{
    u32EXTClockFreq = u32ClockFreq;
}

//**********************************************************************************************************
//
//! \brief  This function is used to get the current counters of the specified timer channel. 
//!
//! \param  ch    E_TIMER_CHANNEL, it could be E_TMR0/E_TMR1/E_TMR2/E_TMR3 
//!
//! \retval u32Counters                             Return current counters                           
//!         E_DRVTIMER_CHANNEL                      Invalid Timer channel 
//
//**********************************************************************************************************
uint32_t DrvTIMER_GetCounters(E_TIMER_CHANNEL ch)
{
    TIMER_TypeDef *base = _getBase(ch);
    return(base->DR);
}

//**********************************************************************************************************
//
//! \brief  Get the version number of Timer/WDT driver. 
//!
//! \param  None  
//!
//! \retval DRVTIMER_VERSION_NUM    Version number of Timer/WDT driver                            
//
//**********************************************************************************************************
uint32_t DrvTIMER_GetVersion(void)
{
    return DRVTIMER_VERSION_NUM;
}

//**********************************************************************************************************
//
//! \brief  Stop timer counter. 
//!
//! \param  ch Timer channel to stop
//!
//! \retval 0                    Success                           
//!         E_DRVTIMER_CHANNEL   Unknown timer channel
//
//**********************************************************************************************************
/**
  * @brief  Stop timer counter
  * @param  ch Timer channel to stop
  * @return Success or not.
  * @retval 0 Success
  * @retval E_DRVTIMER_CHANNEL Unknown timer channel
  */
int32_t DrvTIMER_Stop(E_TIMER_CHANNEL ch)
{

    TIMER_TypeDef *base = _getBase(ch); 
    assert_param(base != NULL); 
    base->CTL_BITS.TMR_EN = 0;
    return(0);
}

//**********************************************************************************************************
//
//! \brief  Set timer frequency. Can use this API to overwrite the one calculate by open() function. 
//!
//! \param  ch Timer channel to configure 
//!
//! \retval 0                     Success                           
//!         E_DRVTIMER_CHANNEL    Unknown timer channel 
//
//**********************************************************************************************************
int32_t DrvTIMER_SetPrescale(E_TIMER_CHANNEL ch, uint8_t prescale)
{

    TIMER_TypeDef *base = _getBase(ch); 
    assert_param(base != NULL); 
    base->PRECNT = prescale;
    return(0);
}

//**********************************************************************************************************
//
//! \brief  Reset specified timer channel. 
//!
//! \param  ch Timer channel to be reset
//!
//! \note   This API blocked until timer reset complete 
//!
//! \retval 0                   Success                           
//!         E_DRVTIMER_CHANNEL  Unknown timer channel  
//
//**********************************************************************************************************
int32_t DrvTIMER_Reset(E_TIMER_CHANNEL ch)
{
    TIMER_TypeDef *base = _getBase(ch); 
    assert_param(base != NULL); 
        
    base->CTL_BITS.SW_RST = 1;
    
    while(base->CTL_BITS.SW_RST == 1);
    return(0);
}

//**********************************************************************************************************
//
//! \brief  Configure specified timer channels to inter timer trigger mode. 
//!
//! \param  ch Timer channel to work on inter timer trigger mode
//! \param  pin External pin to input external clock or event
//!
//! \note   Both channel work in inter timer rtigger mode must be opened before call thins API
//!
//! \retval 0 Success                           
//!         E_DRVTIMER_CHANNEL Unknown support timer channel 
//
//**********************************************************************************************************
int32_t DrvTIMER_EnableInterTimerTriggerMode(E_TIMER_CHANNEL ch)
{
    TIMER_TypeDef *base;
    E_TIMER_CHANNEL _ch = (E_TIMER_CHANNEL)(((uint32_t)ch >> 1) << 1); // now _ch is ether 0 or 2       
        
    base = _getBase(_ch);
    assert_param(base != NULL); 
	    
    base->CTL_BITS.INTR_TRG_EN = 1;

    return(0);
}

//**********************************************************************************************************
//
//! \brief  Disable specified timer channels to work on inter timer trigger mode. 
//!
//! \param  ch Timer channel to stop working on inter timer trigger mode 
//!
//! \retval 0                    Success                           
//!         E_DRVTIMER_CHANNEL   Unknown support timer channel 
//
//**********************************************************************************************************
int32_t DrvTIMER_DisableInterTimerTriggerMode(E_TIMER_CHANNEL ch)
{
    TIMER_TypeDef *base;
    E_TIMER_CHANNEL _ch = (E_TIMER_CHANNEL)(((uint32_t)ch >> 1) << 1); // now _ch is ether 0 or 2

        
    base = _getBase(_ch);
    assert_param(base != NULL); 

    base->CTL_BITS.INTR_TRG_EN = 0;
    return(0);
}

//**********************************************************************************************************
//
//! \brief  Configure specified timer channel to work on counter reset function. 
//!
//! \param  ch Timer channel to work on counter reset mode
//! \param  pin Assign external pin to reset counter
//! \param  edge Assign rising/falling edge to reset counter
//!
//! \retval 0                    Success                           
//!         E_DRVTIMER_CHANNEL   Unknown support timer channel
//
//**********************************************************************************************************
int32_t DrvTIMER_EnableCounterResetMode(E_TIMER_CHANNEL ch, E_TIMER_CAP_EDGE edge)
{
    TIMER_TypeDef *base = _getBase(ch); 
    assert_param(base != NULL); 
    base->CTL_BITS.TCAP_MODE = 1;
    base->CTL_BITS.TCAP_EDGE = edge;
    base->CTL_BITS.TCAP_EN = 1;
    base->CTL_BITS.TCAP_DEB_EN = 1;


    return(0);
}

//**********************************************************************************************************
//
//! \brief  Disablde specified timer channel to work on counter reset function. 
//!
//! \param  ch Timer channel to stop working on counter reset function 
//!
//! \retval 0                     Success                           
//!         E_DRVTIMER_CHANNEL    Unknown support timer channel 
//
//**********************************************************************************************************
int32_t DrvTIMER_DisableCounterResetMode(E_TIMER_CHANNEL ch)
{
    TIMER_TypeDef *base = _getBase(ch); 
    assert_param(base != NULL);  
    base->CTL_BITS.TCAP_EN = 0;
    base->CTL_BITS.TCAP_DEB_EN = 0;
    return(0);

}

//**********************************************************************************************************
//
//! \brief  Configure specified timer channel to work on capture mode. 
//!
//! \param  ch Timer channel to work on capture mode
//! \param  mode Assign capture mode to work on
//! \param  pin Assign external pin to reset counter
//! \param  edge Assign rising/falling edge to trigger capture function
//!
//! \retval 0                    Success                           
//!         E_DRVTIMER_CHANNEL   Unknown support timer channel 
//
//**********************************************************************************************************
int32_t DrvTIMER_EnableCaptureMode(E_TIMER_CHANNEL ch, E_TIMER_CAP_MODE mode, E_TIMER_CAP_EDGE edge)
{
    TIMER_TypeDef *base = _getBase(ch); 
    assert_param(base != NULL);    

    base->CTL_BITS.TCAP_MODE = 0;
    base->CTL_BITS.TCAP_EDGE = edge;
    base->CTL_BITS.CAP_CNT_MOD = mode;
    base->CTL_BITS.TCAP_EN = 1;
    base->CTL_BITS.TCAP_DEB_EN = 1;

    return(0);
}

//**********************************************************************************************************
//
//! \brief  Disablde specified timer channel to work on capture mode. 
//!
//! \param  ch Timer channel to stop working on capture mode 
//!
//! \retval 0                       Success                           
//!         E_DRVTIMER_CHANNEL      Unknown support timer channel 
//
//**********************************************************************************************************
int32_t DrvTIMER_DisableCaptureMode(E_TIMER_CHANNEL ch)  
{
    TIMER_TypeDef *base = _getBase(ch); 
    assert_param(base != NULL);       
    base->CTL_BITS.TCAP_EN = 0;
    base->CTL_BITS.TCAP_DEB_EN = 0;
    
    return(0);
}

//**********************************************************************************************************
//
//! \brief  Configure specified timer channel to work on external event mode. 
//!
//! \param  ch Timer channel to work on external event
//! \param  pin Assign external pin to monitor event
//! \param  edge Assign rising/falling edge to increase counter
//!
//! \retval 0 Success                          
//!         E_DRVTIMER_CHANNEL Unknown support timer channel 
//
//**********************************************************************************************************
int32_t DrvTIMER_EnableExternalEventMode(E_TIMER_CHANNEL ch, E_TIMER_EVENT_EDGE edge)
{
    TIMER_TypeDef *base = _getBase(ch); 
    assert_param(base != NULL); 
        
    base->CTL_BITS.EVENT_EDGE = edge;
    base->CTL_BITS.EVENT_EN = 1;
    base->CTL_BITS.EVNT_DEB_EN = 1;
    
    return(0);
}

//**********************************************************************************************************
//
//! \brief  Disable specified timer channel to work on external event mode. 
//!
//! \param  ch Timer channel to stop working on external event 
//!
//! \retval 0                      Success                           
//!         E_DRVTIMER_CHANNEL     Unknown support timer channel
//
//**********************************************************************************************************
int32_t DrvTIMER_DisableExternalEventMode(E_TIMER_CHANNEL ch)
{
    TIMER_TypeDef *base = _getBase(ch); 
    assert_param(base != NULL); 

    base->CTL_BITS.EVENT_EN = 0;
    base->CTL_BITS.EVNT_DEB_EN = 0;
    
    return(0);

}

//**********************************************************************************************************
//
//! \brief  Enable specified timer channel to wake up the system. 
//!
//! \param  ch Timer channel enabled to wake up system 
//!
//! \retval 0                     Success                          
//!         E_DRVTIMER_CHANNEL    Unknown support timer channel
//
//**********************************************************************************************************
int32_t DrvTIMER_EnableWakeUp(E_TIMER_CHANNEL ch)
{
    TIMER_TypeDef *base = _getBase(ch); 
    assert_param(base != NULL); 
    base->CTL_BITS.WAKE_EN = 1;
    return(0);
}

//**********************************************************************************************************
//
//! \brief  Disable specified timer channel to wake up the system. 
//!
//! \param  ch Timer channel Disabled to wake up system 
//!
//! \retval 0                    Success                           
//!         E_DRVTIMER_CHANNEL   Unknown support timer channel 
//
//**********************************************************************************************************
int32_t DrvTIMER_DisableWakeUp(E_TIMER_CHANNEL ch)
{
    TIMER_TypeDef *base = _getBase(ch); 
    assert_param(base != NULL);    
    base->CTL_BITS.WAKE_EN = 0;
    return(0);
}

//**********************************************************************************************************
//
//! \brief  Get latest captured data of specified timer channel. 
//!
//! \param  ch Timer channel to get latest captured data 
//!
//! \retval E_DRVTIMER_CHANNEL Unknown support timer channel                          
//!         Others Capture data 
//
//**********************************************************************************************************
int32_t DrvTIMER_GetCaptureData(E_TIMER_CHANNEL ch)
{
    TIMER_TypeDef *base = _getBase(ch);
    assert_param(base != NULL);     
    return(base->TCAP);

}

//**********************************************************************************************************
//
//! \brief  Set compare value of specified timer channel. 
//!
//! \param  ch Timer channel to set compare value
//! \param  value Compare value, any value beteen [1~0xFFFFFF]  
//!
//! \retval E_DRVTIMER_CHANNEL Unknown support timer channe                          
//!         E_DRVTIMER_EIO Comare value exceed 0xFFFFFF or less than 2
//
//**********************************************************************************************************
int32_t DrvTIMER_SetCompareValue(E_TIMER_CHANNEL ch, uint32_t value)
{
    TIMER_TypeDef *base = _getBase(ch);
    assert_param(base != NULL);    
    if(value > TIMER_MAX_VAL || value < 2)
        return(E_DRVTIMER_EIO);
    base->CMPR = value;
    return(0);
}

//**********************************************************************************************************
//
//! \brief  Configure TIMERx trigger event. 
//!
//! \param  ch Timer channel
//!
//! \note   This API will clear TIMERx wake up status
//!
//! \retval 0 Success                          
//!         E_DRVTIMER_CHANNEL Unknown support timer channel
//!         E_DRVTIMER_EIO Parameter error
//
//**********************************************************************************************************
/**
  * @brief  Configure TIMERx trigger event
  * @param  ch Timer channel                                                     
  * @parma  event Event to be trigger
  * @param  src Source to trigger ADC/DAC/PDMA
  * @return Success or not
  * @retval 0 Success
  * @retval E_DRVTIMER_CHANNEL Unknown support timer channel
  * @retval E_DRVTIMER_EIO Parameter error
  * @note   This API will clear TIMERx wake up status
  */
int32_t DrvTIMER_ConfigTriggerEvent(E_TIMER_CHANNEL ch, uint8_t event, E_TIMER_TRIGGER_SRC src)
{
    TIMER_TypeDef *base = _getBase(ch);
    
    assert_param(base != NULL); 
        
    if(event > (TIMER_TRIGGER_PDMA | TIMER_TRIGGER_DAC | TIMER_TRIGGER_ADC))
        return(E_DRVTIMER_EIO);
    base->CTL_BITS.CAP_TRG_EN = src;
    base->CTL_BITS.PDMA_TEEN = (event & TIMER_TRIGGER_PDMA) ? 1 : 0;
    base->CTL_BITS.DAC_TEEN = (event & TIMER_TRIGGER_DAC) ? 1 : 0;
    base->CTL_BITS.ADC_TEEN = (event & TIMER_TRIGGER_ADC) ? 1 : 0;  
    
    return(0);
}

//**********************************************************************************************************
//
//! \brief  Check if TIMERx is active or not. 
//!
//! \param  ch Timer channel
//!
//! \retval 0 TIMERx is not active                           
//!         1 TIMERx is active
//!         E_DRVTIMER_CHANNEL Unknown support timer channel
//
//**********************************************************************************************************
int32_t DrvTIMER_IsActive(E_TIMER_CHANNEL ch)
{
    TIMER_TypeDef *base = _getBase(ch);
    assert_param(base != NULL);    
    return(base->CTL_BITS.TMR_ACT);
}

//**********************************************************************************************************
//
//! \brief  Check if TIMERx is CPU wake up source or not. 
//!
//! \param  ch Timer channel
//!
//! \note   This API will clear TIMERx wake up status
//!
//! \retval 0                        TIMERx is not CPU wake up source                           
//!         1                        TIMERx is CPU wake up source
//!         E_DRVTIMER_CHANNEL       Invalid timer channel number 
//
//**********************************************************************************************************
int32_t DrvTIMER_IsWakeUpSource(E_TIMER_CHANNEL ch)
{
    TIMER_TypeDef *base = _getBase(ch);
    int8_t ret;
    assert_param(base != NULL); 
    ret = (base->ISR_BITS.TMR_WAKE_STS == 1) ? 1 : 0;   
    
    if(ret)
        base->ISR = 0x10;
    
    return(ret);
}


/*---------------------------------------------------------------------------------------------------------*/
/* Function:        WDT_IRQHandler                                                                         */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*                  None                                                                                   */
/* Returns:                                                                                                */
/*                  None                                                                                   */
/* Description:                                                                                            */
/*                  The WatchDog Timer(WDT) default IRQ, declared in startup_NUC1xx.s                      */
/*---------------------------------------------------------------------------------------------------------*/
void WDT_IRQHandler(void)
{

    WDT->ISR_BITS.WDT_IS = 1;         /* Clear the WDT INT Flag */

    if (fnCallBack_WDT)        
    {
        fnCallBack_WDT(wdt_param);
    }
}

/*---------------------------------------------------------------------------------------------------------*/
/* Function:        DrvWDT_Open                                                                            */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*                  WDTlevel -[in]                                                                         */
/*                      E_WDT_INTERVAL, enumerate the WDT time-out interval.                               */
/*                      Refer to WDT_INTERVAL enumeration for detail time-out value.                       */
/* Returns:                                                                                                */
/*                  None                                                                                   */
/* Description:                                                                                            */
/*                  Enable WDT engine clock and set WDT time-out interval.                                 */
/*---------------------------------------------------------------------------------------------------------*/
void DrvWDT_Open(E_WDT_INTERVAL WDTlevel)
{
    DrvWDT_Ioctl(E_WDT_IOC_STOP_TIMER, 0);  /* Stop WDT first */

    CLK->APBCLK_BITS.WDT_EN = 1;  /* Enable WatchDog Timer Clock */    

    WDT->CTL_BITS.WTIS = WDTlevel;  /* Select WatchDog Timer Interval */
}

/*---------------------------------------------------------------------------------------------------------*/
/* Function:        DrvWDT_Close                                                                           */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*                  None                                                                                   */
/* Returns:                                                                                                */
/*                  None                                                                                   */
/* Description:                                                                                            */
/*                  The function is used to stop/disable WDT relative functions.                           */
/*---------------------------------------------------------------------------------------------------------*/
void DrvWDT_Close(void)
{
    DrvWDT_Ioctl(E_WDT_IOC_STOP_TIMER, 0);     /* Stop WDT */
    DrvWDT_Ioctl(E_WDT_IOC_DISABLE_INT, 0);    /* Disable WDT Interrupt */

    CLK->APBCLK_BITS.WDT_EN = 0;
}

/*---------------------------------------------------------------------------------------------------------*/
/* Function:        DrvWDT_InstallISR                                                                      */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*                  pvWDTISR - [in]                                                                        */
/*                      The function pointer of the interrupt service routine                              */
/* Returns:                                                                                                */
/*                  None                                                                                   */
/* Description:                                                                                            */
/*                  The function is used to install WDT interrupt service routine.                         */
/*---------------------------------------------------------------------------------------------------------*/
void DrvWDT_InstallISR(WDT_CALLBACK pvWDTISR, uint32_t param)
{
    fnCallBack_WDT = (WDT_CALLBACK)pvWDTISR; 
    wdt_param = param;
    WDT->IER_BITS.WDT_IE = 1 ;  
    
}

/*---------------------------------------------------------------------------------------------------------*/
/* Function:        DrvWDT_Ioctl                                                                           */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*                  uWDTCmd - [in]                                                                         */
/*                      E_WDT_CMD commands, it could be the one of the follow commands                     */
/*                          E_WDT_IOC_START_TIMER,                                                         */
/*                          E_WDT_IOC_STOP_TIMER,                                                          */
/*                          E_WDT_IOC_ENABLE_INT,                                                          */
/*                          E_WDT_IOC_DISABLE_INT,                                                         */
/*                          E_WDT_IOC_ENABLE_WAKEUP,                                                       */
/*                          E_WDT_IOC_DISABLE_WAKEUP,                                                      */
/*                          E_WDT_IOC_RESET_TIMER,                                                         */
/*                          E_WDT_IOC_ENABLE_RESET_FUNC,                                                   */
/*                          E_WDT_IOC_DISABLE_RESET_FUNC,                                                  */
/*                          E_WDT_IOC_SET_INTERVAL                                                         */
/*                  uArgument - [in]                                                                       */
/*                      Set the argument for the specified WDT command                                     */
/* Returns:                                                                                                */
/*                  E_SUCCESS                               Operation successful                           */
/*                  E_DRVWDT_CMD                            Invalid WDT command                            */
/* Description:                                                                                            */
/*                  The function is used to operate more WDT applications, it could be the                 */
/*                  start/stop the WDT, enable/disable WDT interrupt function, enable/disable WDT          */
/*                  time-out wake up function, enable/disable system reset when WDT time-out and           */
/*                  set the WDT time-out interval.                                                         */
/*---------------------------------------------------------------------------------------------------------*/
int32_t DrvWDT_Ioctl(E_WDT_CMD uWDTCmd, uint32_t uArgument)
{
    switch (uWDTCmd)
    {
        case E_WDT_IOC_START_TIMER  :
        {
            WDT->CTL_BITS.WTE = 1;
            break ;
        }

        case E_WDT_IOC_STOP_TIMER :
        {
            WDT->CTL_BITS.WTE = 0;
            break ;
        }

        case E_WDT_IOC_ENABLE_INT :
        {
            WDT->IER_BITS.WDT_IE = 1;
            NVIC_EnableIRQ(WDT_IRQn); 
            break ;
        }

        case E_WDT_IOC_DISABLE_INT :
        {
            WDT->IER_BITS.WDT_IE = 0;
            NVIC_DisableIRQ(WDT_IRQn); 
            break ;
        }

        case E_WDT_IOC_ENABLE_WAKEUP :
        {
            WDT->CTL_BITS.WTWKE = 1;            
            break ;
        }

        case E_WDT_IOC_DISABLE_WAKEUP :
        {
            WDT->CTL_BITS.WTWKE = 0;            
            break ;
        }

        case E_WDT_IOC_RESET_TIMER:
        {
            WDT->CTL_BITS.WTR = 1;
            break ;
        }

        case E_WDT_IOC_ENABLE_RESET_FUNC :
        {
            WDT->CTL_BITS.WTRE = 1;
            break ;
        }

        case E_WDT_IOC_DISABLE_RESET_FUNC:
        {
            WDT->CTL_BITS.WTRE = 0;
            break ;
        }

        case E_WDT_IOC_SET_INTERVAL:
        {
            WDT->CTL_BITS.WTIS = (E_WDT_INTERVAL)uArgument;

            break ;
        }
       
        default :
        {
            assert_param(0);
        }
    }

    return E_SUCCESS;
}
